package Java面试周阳.可见性的代码验证说明;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class Data {
    volatile int number = 0;
    public void addTo60() {
        this.number = 60;
    }
    // 注意number已用voliate修饰，不保证原子性
    public void addPlusPlus() {
        number++;
    }

    AtomicInteger atomicInteger = new AtomicInteger();
    public void addAtomic() {
        atomicInteger.getAndIncrement();
    }
}

/**
 * 1 验证voliate的可见性
 *  1.1 假如int number = 0，number变量前没有加voliate关键字，没有可见性
 *  1.2 添加了voliate，可以解决可见性问题
 *
 * 2 验证volatile不保证原子性
 *  2.1 原子性指的什么意思？
 *      不可分割，完整性，也即某个线程正在做某个具体业务时，中间不可以被加载或者被分割，需要整体完整，要么同时成功，要么同时失败
 *  2.2 voliatile不保证原子性的案例演示
 *  2.3 为什么不保证原子性？
 *  2.4 如何解决原子性
 *      加sync锁
 *      atomicInteger
 */
public class VoliateDemo {
    public static void main(String[] args) {
//        seeOkByVoliate();
//        verifyAtomic();
        solveByAtomic();
    }

    /**
     * 使用原子类解决原子性问题
     */
    private static void solveByAtomic() {
        Data myData = new Data();
        for(int i=1; i<=20; i++) {
            new Thread(() -> {
                for (int j=1; j<=1000; j++) {
                    myData.addAtomic();
                }
            }, String.valueOf(i)).start();
        }
        // 需要等待上面20个线程全部都计算完成后，再用main线程取得最终的结果值看是多少
        // 默认后台两个线程，垃圾回收线程和main线程
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + "\t atomic type,finally number value:" + myData.atomicInteger);
    }

    /**
     * 验证原子性
     */
    private static void verifyAtomic() {
        Data myData = new Data();
        for(int i=1; i<=20; i++) {
            new Thread(() -> {
                for (int j=1; j<=1000; j++) {
                    myData.addPlusPlus();
                }
            }, String.valueOf(i)).start();
        }
        // 需要等待上面20个线程全部都计算完成后，再用main线程取得最终的结果值看是多少
        // 默认后台两个线程，垃圾回收线程和main线程
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + "\t int type,finally number value:" + myData.number);
    }

    /**
     * voliate保证原子性，及时通知其他线程，主内存的数据已经修改
     */
    private static void seeOkByVoliate() {
        Data myData = new Data();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t come in");
            // 暂停一会线程
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myData.addTo60();
            System.out.println(Thread.currentThread().getName() + "\t updated number value is " +myData.number);
        }, "AAA").start();

        // 第二个线程是main线程
        while (myData.number == 0){
            // main线程一直在这里等待，直至number不等于0
        }
        System.out.println(Thread.currentThread().getName() + "\t mission is over. number value is:" + myData.number);
    }

}
